using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Caching;

namespace memcached
{
    public class ObjectCache : System.Runtime.Caching.ObjectCache
    {
        public static readonly TimeSpan DefaultExpiration = TimeSpan.FromMinutes(60.0);

        private static readonly DefaultCacheCapabilities capabilities =
            DefaultCacheCapabilities.OutOfProcessProvider | DefaultCacheCapabilities.AbsoluteExpirations;
        private static readonly string cacheName = "memcached.ObjectCache";

        private readonly ICache client;

        public override DefaultCacheCapabilities DefaultCacheCapabilities
        {
            get
            {
                return capabilities;
            }
        }

        public override string Name
        {
            get
            {
                return cacheName;
            }
        }

        public override object this[string key]
        {
            get
            {
                bool success;
                return client.Get(key, out success);
            }
            set
            {
                client.Set(key, DateTime.Now.Add(DefaultExpiration), value);
            }
        }

        /// <summary>
        /// Use provided cache store.
        /// </summary>
        /// <remarks>Note the instance will not dispose the store.</remarks>
        /// <param name="store"></param>
        public ObjectCache(ICache store)
        {
            if (store == null)
                throw new ArgumentNullException("store");
            client = store;
        }

        public override object AddOrGetExisting(
            string key, object value, CacheItemPolicy policy, string regionName = null)
        {
            if (policy == null)
                throw new ArgumentNullException("policy");

            return this.AddOrGetExisting(key, value, policy.AbsoluteExpiration, (string)null);
        }

        public override CacheItem AddOrGetExisting(CacheItem value, CacheItemPolicy policy)
        {
            if (value == null)
                throw new ArgumentNullException("value");
            if (policy == null)
                throw new ArgumentNullException("policy");
            object existing = this.AddOrGetExisting(value.Key, value.Value, policy.AbsoluteExpiration, (string)null);
            CacheItem cacheItem = (CacheItem)null;
            if (existing != null)
                cacheItem = new CacheItem(value.Key, existing);
            return cacheItem;
        }

        public override object AddOrGetExisting(
            string key, object value, DateTimeOffset absoluteExpiration, string regionName = null)
        {
            if (string.IsNullOrWhiteSpace(key))
                throw new ArgumentNullException("key");
            if (value == null)
                throw new ArgumentNullException("value");
            bool success;
            object v = client.Get(key, out success);
            if (v != null)
                return v;
            success = this.client.Add(key, absoluteExpiration.DateTime, value);
            return null;
        }

        public override bool Contains(string key, string regionName = null)
        {
            return this.Get(key, (string)null) != null;
        }

        public override CacheEntryChangeMonitor CreateCacheEntryChangeMonitor(
            IEnumerable<string> keys, string regionName = null)
        {
            throw new NotSupportedException();
        }

        public override object Get(string key, string regionName = null)
        {
            if (string.IsNullOrWhiteSpace(key))
                throw new ArgumentNullException("key");
            bool success;
            return client.Get(key, out success);
        }

        public override CacheItem GetCacheItem(string key, string regionName = null)
        {
            if (string.IsNullOrWhiteSpace(key))
                throw new ArgumentNullException("key");
            bool success;
            object v = client.Get(key, out success);
            CacheItem cacheItem = null;
            if (success && v != null)
                cacheItem = new CacheItem(key, v);
            return cacheItem;
        }

        public override long GetCount(string regionName = null)
        {
            throw new NotSupportedException();
        }

        protected override IEnumerator<KeyValuePair<string, object>> GetEnumerator()
        {
            throw new NotSupportedException();
        }

        public override IDictionary<string, object> GetValues(IEnumerable<string> keys, string regionName = null)
        {
            throw new NotSupportedException();
        }

        public override object Remove(string key, string regionName = null)
        {
            if (string.IsNullOrWhiteSpace(key))
                throw new ArgumentNullException("key");
            object obj = this.Get(key, (string)null);
            if (obj != null)
            {
                client.Remove(key);
            }
            return obj;
        }

        public override void Set(string key, object value, CacheItemPolicy policy, string regionName = null)
        {
            if (value == null)
                throw new ArgumentNullException("value");
            this.Set(key, value, policy.AbsoluteExpiration, (string)null);
        }

        public override void Set(CacheItem item, CacheItemPolicy policy)
        {
            if (item == null)
                throw new ArgumentNullException("item");
            if (policy == null)
                throw new ArgumentNullException("policy");
            this.Set(item.Key, item.Value, (DateTimeOffset)policy.AbsoluteExpiration.DateTime, (string)null);
        }

        public override void Set(string key, object value, DateTimeOffset absoluteExpiration, string regionName = null)
        {
            if (string.IsNullOrWhiteSpace(key))
                throw new ArgumentNullException("key");
            if (value == null)
                throw new ArgumentNullException("value");
            client.Set(key, absoluteExpiration.DateTime, value);
        }
    }
}